#pragma once

#include <iostream>
#include <unistd.h>

#include <memory>
#include <filesystem> // c++17
#include <fstream>

#include "Mutex.hpp"

namespace LogModule
{
    using namespace LockModule;

    class Log
    {
    public:
        Log(){};
        ~Log(){};
    };

    class LogStrategy
    {
    public:
        virtual void SyncLog(const std::string &) = 0;
        virtual ~LogStrategy() = default; // 基类的析构提供虚函数，防止基类多态时属于派生类资源的不正确释放
    };

    class ConsoleLogStrategy : public LogStrategy
    {
    public:
        ConsoleLogStrategy() {}
        void SyncLog(const std::string &msg) override
        {
            LockGuard lock(_mutex);
            std::cout << msg << std::endl;
        }
    private:
        Mutex _mutex;
    };

    const static std::string default_log_filename = "log.txt";
    const static std::string default_log_path = "./log/";// 最后的斜杠也要加上


    class FileLogStrategy : public LogStrategy
    {
    public:
        FileLogStrategy(const std::string logpath = default_log_path, const std::string logname = default_log_filename)
            : _path(logpath), _filename(logname)
        {
            // 文件也属于公共资源
            LockGuard lock(_mutex);

            if(std::filesystem::exists(_path)) { return; }
            try
            {
                std::filesystem::create_directories(_path);
            }
            catch(std::filesystem::filesystem_error &e)
            {
                std::cerr << e.what() << std::endl;
            }
        }
        void SyncLog(const std::string &msg) override
        {
            LockGuard lock(_mutex);
            std::string log = _path + _filename;
            std::ofstream out(log, std::ios::app); // 没有引用 <fstream> 会有不完整报错
            if(!out.is_open())
            {
                return;
            }
            out << msg << std::endl;
            out.close();
        }
    private:
        std::string _path;
        std::string _filename;
        
        Mutex _mutex;
    };

    enum class LogLevel{
        DEBUG = 1,
        INFO,
        WARNING,
        ERROR,
        FATAL,
    };

    std::string Level2String(LogLevel level)
    {
        switch(level)
        {
        case LogLevel::DEBUG:
            return "DEBUG";
        case LogLevel::INFO:
            return "INFO";
        case LogLevel::WARNING:
            return "WARNING";
        case LogLevel::ERROR:
            return "ERROR";
        case LogLevel::FATAL:
            return "FATAL";
        default:
            return "None";
        }
    }

    class Logger
    {
    public:
        Logger()
        {
            _strategy = std::make_shared<ConsoleLogStrategy>();
        }
        void EnableConsoleLog()
        {
            _strategy = std::make_shared<ConsoleLogStrategy>();
        }
        void EnableFileLog()
        {
            _strategy = std::make_shared<FileLogStrategy>();
        }
        ~Logger(){}
        
        // 组织日志信息和格式的工具类
        // 格式 [可读性很好的时间] [⽇志等级] [进程pid] [打印对应⽇志的⽂件名][⾏号] - 消息内容，⽀持可变参数
        class LogMessage
        {
        private:
            std::string CurrentTime()
            {
                time_t time_stamp = ::time(nullptr);
                struct tm currentTime;
                localtime_r(&time_stamp, &currentTime);
                char buffer[1024];

                snprintf(buffer, sizeof(buffer), "%4d-%02d-%02d %02d:%02d:%02d"
                    , currentTime.tm_year + 1900
                    , currentTime.tm_mon + 1
                    , currentTime.tm_mday 
                    , currentTime.tm_hour
                    , currentTime.tm_min
                    , currentTime.tm_sec
                );
                return buffer;
            }
        public:
            // 需要传参构造，行号我LogMessage类不知道 需要别人传
            LogMessage(LogLevel level, std::string filename, int line, Logger &logger)
                : _line(line)
                , _pid(getpid())
                , _level(level)
                , _time(CurrentTime())
                , _filename(filename)
                , _logger(logger)
            {
                std::stringstream ssbuffer;
                ssbuffer << "[" << _time << "] "
                         << "[" << Level2String(_level) << "] "
                         << "[" << _pid << "] "
                         << "[" << _filename << "] "
                         << "[" << _line << "] - ";
                _msg_info = ssbuffer.str();
            }
            template<class T>
            LogMessage &operator<<(const T msg)
            {
                std::stringstream ss;
                ss << msg;
                _msg_info += ss.str();
                return *this;
            }
            ~LogMessage()
            {
                if(_logger._strategy){ _logger._strategy->SyncLog(_msg_info); } //多态
            }
        private:
            LogLevel _level;
            int _pid;
            int _line;
            std::string _time;
            std::string _filename;

            Logger &_logger;        // 负责根据不同的策略刷新
            std::string _msg_info;  // 负责一条日志的信息
        };// end of LogMessage
    LogMessage operator()(LogLevel level, const std::string &filename, int line)
    {
        return LogMessage(level, filename, line, *this);
    }

    private:
        std::shared_ptr<LogStrategy> _strategy;
    }logger;

    #define LOG(level)          logger(level, __FILE__, __LINE__)
    #define ENABLE_FILE()       logger.EnableFileLog()
    #define ENABLE_CONSOLE()    logger.EnableConsoleLog()
    #define LOG_FILE ENABLE_FILE()
    #define LOG_CONSOLE ENABLE_CONSOLE()

}
